//OpenALを用いたドップラー効果のサンプル

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#include <ALUT/alut.h>
#include "keyboard.h"

ALuint Buffer;// Buffers hold sound data.
ALuint Source;// Sources are points of emitting sound.

/* 設定 */
//-- 音源(Source)の設定
ALfloat pitch = 1.0f;
ALfloat gain = 1.0f;
ALfloat SourcePos[] = { 0.0f, 0.0f, -50.0f };// Position of the source sound.
ALfloat SourceVel[] = { 0.0f, 0.0f, 40.0f };// Velocity of the source sound.
//単純にPosition += Velocityとすると時間が早すぎるので適当に補正値を掛けてみたり
//Position += revise*Velocity
const float revise = 0.000003;

//-- 観測者(Listener)の設定
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };// Position of the listener.
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };// Velocity of the listener.
// Orientation of the listener. (first 3 elements are "at", second 3 are "up")
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };

//-- その他の設定
ALfloat SourceMaxDist = 80.0;//これ以上離れると初期位置に戻る

//-- ドップラー効果の設定
//shift = DOPPLER_FACTOR * freq * (DOPPLER_VELOCITY - listener.velocity) / (DOPPLER_VELOCITY + source.velocity)
#define DOPPLER_FACTOR 0.5f
#define DOPPLER_VELOCITY 10.0f


/*
 * ALboolean LoadALData()
 *wavデータの読み込みと音源の初期設定
 */
ALboolean LoadALData()
{
	ALenum  format;
	ALsizei   size;
	ALvoid*   data;
	ALsizei   freq;
	ALboolean loop;

	// Load wav data into a buffer.
	alGenBuffers(1, &Buffer);
	if(alGetError() != AL_NO_ERROR) return AL_FALSE;
	//wavファイルの読み込み
	alutLoadWAVFile((ALbyte*)"wavdata/Ambulance.wav", &format, &data, &size, &freq);
	alBufferData(Buffer, format, data, size, freq);
	alutUnloadWAV(format, data, size, freq);

	// Bind the buffer with the source.
	alGenSources(1, &Source);

	if(alGetError() != AL_NO_ERROR) return AL_FALSE;
	
	//ソースの初期設定
	alSourcei (Source, AL_BUFFER,   Buffer   );//音源
	alSourcef (Source, AL_PITCH,    pitch     );//ピッチ
	alSourcef (Source, AL_GAIN,     gain    );//ゲイン
	alSourcefv(Source, AL_POSITION, SourcePos);//座標
	alSourcefv(Source, AL_VELOCITY, SourceVel);//移動
	alSourcei (Source, AL_LOOPING,  AL_TRUE  );//繰り返す

	// Do an error check and return.
	if(alGetError() != AL_NO_ERROR) return AL_FALSE;

	return AL_TRUE;
}

/*
 * void SetListenerValues()
 * 観測者の設定
 */
void SetListenerValues()
{
	alListenerfv(AL_POSITION,    ListenerPos);
	alListenerfv(AL_VELOCITY,    ListenerVel);
	alListenerfv(AL_ORIENTATION, ListenerOri);
}

/*
 * void KillALData()
 * お掃除
 */
void KillALData()
{
	alDeleteBuffers(1, &Buffer);
	alDeleteSources(1, &Source);
	alutExit();
}

int main(int argc, char *argv[]) {
	int i;//カウンタ
	ALboolean far=AL_FALSE;//遠いかどうかのフラグ
	
	printf("Doppler Effect\n");
	printf("(Press any key to quit.)\n");

	// 初期化
	alutInit(NULL,0);
	alGetError();

	// ソースの設定
	if(LoadALData() == AL_FALSE)
	{
	    printf("Error loading data.");
		return 0;
	}
	
	//観測者の設定
	SetListenerValues();

	// お掃除関数の指定
	atexit(KillALData);

	// 再生開始
	alSourcePlay(Source);
	
	//初期値を保存
	ALfloat SourceDefPos[] = { SourcePos[0], SourcePos[1], SourcePos[2] };
	
	//ドップラー効果の設定
	alDopplerFactor(DOPPLER_FACTOR);
	alDopplerVelocity(DOPPLER_VELOCITY);
	
	while(!kbhit()){
		far=AL_FALSE;
		
		for(i=0;i<3;i++){
			//座標を更新
			SourcePos[i] += revise*SourceVel[i];
			//遠い？
			if( sqrt(abs(pow(SourcePos[i],2)+pow(ListenerPos[i],2)) ) > SourceMaxDist) far = AL_TRUE;
		}
		//遠かったらはじめの位置に戻る
		if(far) for(i=0;i<3;i++) SourcePos[i]=SourceDefPos[i];
		
		alSourcefv(Source, AL_POSITION, SourcePos);
	}
	return 0;
}
